﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LoA.CameraEngine.Entities;
using LoA.Shared.Interfaces;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace LoA.CameraEngine {
	public partial class CameraComponent : GameComponent, ICamera {
		private static CameraComponent _current = null;
		public static CameraComponent Current { get { return _current; } }

		IInput InputEngine;
		public IWorldSky WorldSky { get; private set; }
		ICameraProcessor Processor;

		public CameraComponent(Game game, IInput inputEngine, IWorldSky worldSky)
			: base(game) {

			if (_current == null)
				_current = this;

			this.InputEngine = inputEngine;
			this.WorldSky = worldSky;
			this.Processor = this.CreateProcessor(CameraMode.SecondPerson);

			game.IsMouseVisible = true;
		}

		private ICameraProcessor CreateProcessor(CameraMode mode) {
			switch (mode) {
				case CameraMode.God:
					return new GodCameraProcessor(Game);
				case CameraMode.FirstPerson:
					return new FirstPersonCameraProcessor(Game);
				case CameraMode.SecondPerson:
					return new SecondPersonCameraProcessor(Game);
				case CameraMode.SecondPersonDynamic:
					return new SecondPersonDynamicCameraProcessor(Game);
				case CameraMode.SecondPersonSpring:
					return new SecondPersonSpringCameraProcessor(Game);
			}
			return new FreeCameraProcessor(Game);
		}

		public Matrix ViewMatrix { get { return Processor.ViewMatrix; } }
		public Matrix ProjectionMatrix { get { return this.Processor.ProjectionMatrix; } }

		public override void Initialize( ) {
			this.Suspended = false;
			base.Initialize( );

			this.Processor.AspectRatio = (float)this.Game.GraphicsDevice.Viewport.Width / this.Game.GraphicsDevice.Viewport.Height;
		}

		public void UpdateTarget(Vector3 Position, Matrix Direction) {
			if (this.Processor is FirstPersonCameraProcessor) {
				var processor = (FirstPersonCameraProcessor)this.Processor;
				processor.ObjectPosition = Position;
				processor.ObjectDirection = Direction.Forward;
				processor.ObjectUp = Direction.Up;

				//processor.ObjectDirection = Vector3.Transform(processor.ObjectDirection, Matrix.CreateRotationZ(this.objectDirectionBuffer.Z));
				//processor.ObjectDirection = Vector3.Transform(processor.ObjectDirection, Matrix.CreateRotationY(this.objectDirectionBuffer.Y));
			}
			if (this.Processor is SecondPersonCameraProcessor) {
				var processor = (SecondPersonCameraProcessor)this.Processor;
				processor.ObjectPosition = Position;
				processor.ObjectDirection = Direction.Forward;
				processor.ObjectUp = Direction.Up;

				//processor.ObjectDirection = Vector3.Transform(processor.ObjectDirection, Matrix.CreateRotationZ(this.objectDirectionBuffer.Z));
				//processor.ObjectDirection = Vector3.Transform(processor.ObjectDirection, Matrix.CreateRotationY(this.objectDirectionBuffer.Y));
			}
			if (this.Processor is SecondPersonSpringCameraProcessor) {
				var processor = (SecondPersonSpringCameraProcessor)this.Processor;
				processor.ObjectPosition = Position;
				processor.ObjectDirection = Direction.Forward;
				processor.ObjectUp = Direction.Up;

				//processor.ObjectDirection = Vector3.Transform(processor.ObjectDirection, Matrix.CreateRotationZ(this.objectDirectionBuffer.Z));
				//processor.ObjectDirection = Vector3.Transform(processor.ObjectDirection, Matrix.CreateRotationY(this.objectDirectionBuffer.Y));
			}
		}

		public bool Suspended { get; private set; }
		public void Suspend( ) { this.Suspended = true; }
		public void Resume( ) { this.Suspended = false; }

		public TimeSpan DayTime { get { return this.dayTime; } }
		TimeSpan dayTime = new TimeSpan(0, 0, 5);
		private void UpdateDayTime( ) {
			dayTime = dayTime.Add(new TimeSpan(0, 0, 10));
			dayTime = dayTime.Add(new TimeSpan(dayTime.Days * -24, 0, 0));
		}
		private float GetLightPower( ) {
			float hour = (float)dayTime.TotalHours - 12;
			hour = Math.Sign(hour) * hour;
			hour += 1;

			float p = 0.083f * (13 - hour);

			if (hour >= 5) {
				if (hour >= 7) {
					p = -0.02f * hour + 0.74f;
					//y=-0,02x+0,74 dla 7<=x<12
				} else
					p = -0.15f * hour + 1.65f;
				//y=-0,15x+1,65 dla 5<=x<7
			} else
				p = -0.025f * hour + 1.025f;
			//y=-0,025x+1,025 dla 1<x<5

			var nowy = Vector3.Transform(new Vector3(0, 0.5f, 0), Matrix.CreateRotationX(MathHelper.ToRadians(hour * 15)));
			p = nowy.Y + 0.5f;

			p *= 0.75f;
			p += 0.25f;
			return p;
		}
		public void SetLighting(SkinnedEffect effect) {
			// Set default lighting
			if (7 <= DayTime.Hours && DayTime.Hours < 20)
				effect.EnableDefaultLighting( );
			effect.PreferPerPixelLighting = true;

			float lighting = GetLightPower( );
			//effect.DiffuseColor = new Vector3(lighting, lighting, lighting);

			// Set the fog to match the black background color
			//effect.FogEnabled = true;
			//effect.FogColor = Vector3.Zero;
			//effect.FogStart = 1000;
			//effect.FogEnd = 3200;
		}
		public void SetLighting(BasicEffect effect) {
			// Set default lighting
			if (7 <= DayTime.Hours && DayTime.Hours < 20)
				effect.EnableDefaultLighting( );
			effect.PreferPerPixelLighting = true;

			float lighting = GetLightPower( );
			//effect.DiffuseColor = new Vector3(lighting, lighting, lighting);

			// Set the fog to match the black background color
			//effect.FogEnabled = true;
			//effect.FogColor = Vector3.Zero;
			//effect.FogStart = 1000;
			//effect.FogEnd = 3200;
		}
	}
}